package com.simp.aop;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.github.benmanes.caffeine.cache.Cache;
import com.simp.costant.Constant;
import com.simp.expection.GlobalException;
import com.simp.utils.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author pan.shun
 * @since 2022/3/17 10:30
 */
@Aspect
@Component
@Slf4j
public class PermissionAspect {

    @Autowired
    private Cache<String, Object> caffeineCache;

    //切入点表达式决定了用注解方式的方法切仍是针对某个路径下的全部类和方法进行切，方法必须是返回void类型
    @Pointcut("@annotation(com.simp.aop.PermissionRole)")
    private void permissionCheckCut() {
    }

    @SuppressWarnings("unchecked")
    private List<String> getRoleList(String userName) {
        List<String> roleList = new ArrayList<>();
        @Nullable Object ifPresent = caffeineCache.getIfPresent("ROLE_" + userName);
        if (ifPresent != null) {
            roleList.addAll((Collection<? extends String>) ifPresent);
        }
        return roleList;
    }

    @Around("permissionCheckCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //1.记录日志信息
        Signature signature = pjp.getSignature();
        String className = pjp.getTarget().getClass().getSimpleName();
        String methodName = signature.getName();
        log.info("className:{},methodName:{}", className, methodName);

        //2.角色权限校验
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        if (targetMethod.isAnnotationPresent(PermissionRole.class)) {
            //获取方法上注解中代表的权限
            PermissionRole permission = targetMethod.getAnnotation(PermissionRole.class);
            String role = permission.role();
            log.info("当前接口请求的用户角色role:{}", role);
            if (StringUtils.isNotEmpty(role)) {
                String[] roles = role.split(",");//接口容许的角色
                List<String> list = Arrays.asList(roles);
                //若是该接口只容许老师角色访问。则要获取当前用户是否是老师角色。
                HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                String token = request.getHeader(Constant.TOKEN_CONFIG.TOKEN_KEY);

                String userName = JwtUtil.getJwtClaimsDetail(token).getUserName();
                List<String> roleList = getRoleList(userName);

                List<String> collect = roleList.stream().filter(list::contains)
                        .collect(Collectors.toList());

                if (collect.size() != 0) {
                    //3.执行业务逻辑，放行
                    return pjp.proceed();
                } else {
                    //若是没有权限,抛出异常,由Spring框架捕获,跳转到错误页面
                    throw new GlobalException("权限不足");
                }
            }
        }
        throw new GlobalException("权限不足");
    }
}
